home *** CD-ROM | disk | FTP | other *** search
/ Scene 96 / Scene 96 International Edition (Zyklop Software) (Disc 2) (1997).iso / misc / coding / midas060 / src / mixsd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-01-16  |  19.3 KB  |  699 lines

  1. /*      MIXSD.C
  2.  *
  3.  * Miscellaneous helper functions common to all mixing sound devices.
  4.  * Technically these functions should be part of each Sound Device's internal
  5.  * code, but are here to save some space and help maintenance.
  6.  *
  7.  * $Id: mixsd.c,v 1.2 1997/01/16 18:41:59 pekangas Exp $
  8.  *
  9.  * Copyright 1996,1997 Housemarque Inc.
  10.  *
  11.  * This file is part of the MIDAS Sound System, and may only be
  12.  * used, modified and distributed under the terms of the MIDAS
  13.  * Sound System license, LICENSE.TXT. By continuing to use,
  14.  * modify or distribute this file you indicate that you have
  15.  * read the license and understand and accept it fully.
  16. */
  17.  
  18. #include "lang.h"
  19. #include "mtypes.h"
  20. #include "errors.h"
  21. #include "mmem.h"
  22. #include "mixsd.h"
  23. #include "sdevice.h"
  24. #include "dsm.h"
  25. #include "dma.h"
  26.  
  27. RCSID(const char *mixsd_rcsid = "$Id: mixsd.c,v 1.2 1997/01/16 18:41:59 pekangas Exp $";)
  28.  
  29.  
  30. //#define DUMPBUFFER
  31.  
  32.  
  33.  
  34. #define MIX8BITS 12                     /* number of bits of accuracy in
  35.                                            mixing for 8-bit output */
  36.  
  37. unsigned dmaBufferSize;                 /* DMA playback buffer size */
  38. //static dmaBuffer buffer;                /* DMA playback buffer */
  39. dmaBuffer buffer;
  40. static unsigned mixRate;                /* mixing rate */
  41. static unsigned outputMode;             /* output mode */
  42. static unsigned amplification;          /* amplification level */
  43. static unsigned updateMix;              /* number of elements to mix between
  44.                                            two updates */
  45. static unsigned mixLeft;                /* number of elements to mix before
  46.                                            next update */
  47. //static unsigned dmaPos;                 /* DMA position inside buffer */
  48. //static unsigned mixPos;                 /* mixing position */
  49. unsigned dmaPos, mixPos;
  50. int             playDMA;                /* Playing through DMA -flag */
  51. static uchar    *ppTable;               /* post-processing table for 8-bit
  52.                                            output */
  53.  
  54. #ifdef DUMPBUFFER
  55. #include <stdio.h>
  56. static FILE     *f;
  57. #endif
  58.  
  59.  
  60.  
  61.  
  62. /****************************************************************************\
  63. *
  64. * Function:     static unsigned CALLING (*postProc)(unsigned numElements,
  65. *                   uchar *bufStart, unsigned mixPos, unsigned *mixBuffer,
  66. *                   uchar *ppTable);
  67. *
  68. * Description:  Pointer to the actual post-processing routine. Takes
  69. *               DSM output elements from dsmMixBuffer and writes them to
  70. *               DMA buffer at *bufStart in a format suitable for the Sound
  71. *               Device.
  72. *
  73. * Input:        unsigned numElements    number of elements to process
  74. *                                       (guaranteed to be even)
  75. *               uchar *bufStart         pointer to start of DMA buffer
  76. *               unsigned mixPos         mixing position in DMA buffer
  77. *               unsigned *mixBuffer     source mixing buffer
  78. *               uchar *ppTable          pointer to post-processing table
  79. *
  80. * Returns:      New mixing position in DMA buffer. Can not fail.
  81. *
  82. \****************************************************************************/
  83.  
  84. static unsigned CALLING (*postProc)(unsigned numElements, uchar *bufStart,
  85.     unsigned mixPos, unsigned *mixBuffer, uchar *ppTable);
  86.  
  87.  
  88.  
  89.  
  90. /****************************************************************************\
  91. *
  92. * Function:     unsigned pp16Mono();
  93. *
  94. * Description:  16-bit mono post-processing routine
  95. *
  96. \****************************************************************************/
  97.  
  98. unsigned CALLING pp16Mono(unsigned numElements, uchar *bufStart,
  99.     unsigned mixPos, unsigned *mixBuffer, uchar *ppTable);
  100.  
  101.  
  102.  
  103.  
  104. /****************************************************************************\
  105. *
  106. * Function:     unsigned pp8Mono();
  107. *
  108. * Description:  8-bit mono post-processing routine
  109. *
  110. \****************************************************************************/
  111.  
  112. unsigned CALLING pp8Mono(unsigned numElements, uchar *bufStart,
  113.     unsigned mixPos, unsigned *mixBuffer, uchar *ppTable);
  114.  
  115.  
  116.  
  117.  
  118. /****************************************************************************\
  119. *
  120. * Function:     unsigned pp16Stereo();
  121. *
  122. * Description:  16-bit stereo post-processing routine
  123. *
  124. \****************************************************************************/
  125.  
  126. unsigned CALLING pp16Stereo(unsigned numElements, uchar *bufStart,
  127.     unsigned mixPos, unsigned *mixBuffer, uchar *ppTable);
  128.  
  129.  
  130.  
  131.  
  132. /****************************************************************************\
  133. *
  134. * Function:     unsigned pp8Stereo();
  135. *
  136. * Description:  8-bit stereo post-processing routine
  137. *
  138. \****************************************************************************/
  139.  
  140. unsigned CALLING pp8Stereo(unsigned numElements, uchar *bufStart,
  141.     unsigned mixPos, unsigned *mixBuffer, uchar *ppTable);
  142.  
  143.  
  144.  
  145.  
  146. /****************************************************************************\
  147. *
  148. * Function:     int mixsdInit(unsigned mixRate, unsigned mode, unsigned
  149. *                   dmaChNum)
  150. *
  151. * Description:  Common initialization for all mixing Sound Devices.
  152. *               Initializes DMA functions, DSM, starts DMA playback and
  153. *               allocates memory for possible post-processing tables
  154. *
  155. * Input:        unsigned mixRate        mixing rate in Hz
  156. *               unsigned mode           output mode
  157. *               int dmaChNum            DMA channel number / -1 if not to be
  158. *                                       played yet
  159. *
  160. * Returns:      MIDAS error code
  161. *
  162. \****************************************************************************/
  163.  
  164. int CALLING mixsdInit(unsigned _mixRate, unsigned mode, int dmaChNum)
  165. {
  166.     int         error;
  167.     int         mixMode;
  168.     U8          *p, val;
  169.     unsigned    i;
  170.  
  171.     mixRate = _mixRate;
  172.     outputMode = mode;
  173.     mixPos = 0;
  174.  
  175.     /* Calculate required DMA buffer size: (1/25th of a second) */
  176.     dmaBufferSize = mixRate / DMABUFLEN;
  177. //    dmaBufferSize = mixRate / 10;
  178.  
  179.     /* Multiply by 2 if stereo: */
  180.     if ( mode & sdStereo )
  181.         dmaBufferSize *= 2;
  182.  
  183.     /* Multiply by 2 if 16-bit: */
  184.     if ( mode & sd16bit )
  185.         dmaBufferSize *= 2;
  186.  
  187.     /* Make buffer length a multiple of 16: */
  188.     dmaBufferSize = (dmaBufferSize + 15) & 0xFFF0;
  189.  
  190.     /* Point postProc() to correct post-processing routine: */
  191.     switch ( mode )
  192.     {
  193.         case (sd16bit | sdMono):
  194.             postProc = &pp16Mono;
  195.             break;
  196.  
  197.         case (sd8bit | sdMono):
  198.             postProc = &pp8Mono;
  199.             break;
  200.  
  201.         case (sd16bit | sdStereo):
  202.             postProc = &pp16Stereo;
  203.             break;
  204.  
  205.         case (sd8bit | sdStereo):
  206.             postProc = &pp8Stereo;
  207.             break;
  208.  
  209.         default:
  210.             ERROR(errInvalidArguments, ID_mixsdInit);
  211.             return errInvalidArguments;
  212.     }
  213.  
  214.     /* Allocate DMA buffer: */
  215.     if ( (error = dmaAllocBuffer(dmaBufferSize, &buffer)) != OK )
  216.         PASSERROR(ID_mixsdInit)
  217.  
  218.     /* Now clear the DMA buffer to avoid nasty clicks */
  219.  
  220.     /* Check zero value: */
  221.     if ( mode & sd8bit )
  222.         val = 0x80;
  223.     else
  224.         val = 0;
  225.  
  226.     /* And clear it: */
  227.     for ( p = (U8*) buffer.dataPtr, i = 0; i < dmaBufferSize; i++,
  228.         *(p++) = val );
  229.  
  230.     /* Allocate memory for post-processing table if necessary: */
  231.     if ( mode & sd8bit )
  232.     {
  233.         /* Allocate memory for 8-bit output mode post-processing table: */
  234.         if ( (error = memAlloc((1 << MIX8BITS), (void**) &ppTable)) != OK )
  235.             PASSERROR(ID_mixsdInit);
  236.     }
  237.     else
  238.         ppTable = NULL;
  239.  
  240.     /* Check correct mixing mode: */
  241.     if ( mode & sdStereo )
  242.         mixMode = dsmMixStereo;
  243.     else
  244.         mixMode = dsmMixMono;
  245.  
  246.     /* Initialize Digital Sound Mixer: */
  247.     if ( mode & sd16bit )
  248.     {
  249.         if ( (error = dsmInit(mixRate, mixMode, 16)) != OK )
  250.             PASSERROR(ID_mixsdInit)
  251.     }
  252.     else
  253.     {
  254.         if ( (error = dsmInit(mixRate, mixMode, MIX8BITS)) != OK )
  255.             PASSERROR(ID_mixsdInit)
  256.     }
  257.  
  258.  
  259.     if ( dmaChNum != -1 )
  260.     {
  261.         /* Start playing the DMA buffer: */
  262.         if ( (error = dmaPlayBuffer(&buffer, dmaChNum, 1)) != OK )
  263.             PASSERROR(ID_mixsdInit)
  264.         playDMA = 1;
  265.     }
  266.     else
  267.     {
  268.         playDMA = 0;
  269.     }
  270.  
  271.     /* Set update rate to 50Hz: */
  272.     if ( (error = mixsdSetUpdRate(5000)) != OK )
  273.         PASSERROR(ID_mixsdInit)
  274.  
  275. #ifdef DUMPBUFFER
  276.     f = fopen("dmabuffer.smp", "wb");
  277. #endif
  278.  
  279.     return OK;
  280. }
  281.  
  282.  
  283.  
  284.  
  285. /****************************************************************************\
  286. *
  287. * Function:     int CALLING mixsdClose(void)
  288. *
  289. * Description:  Common uninitialization code for all mixing Sound Devices.
  290. *               Uninitializes DMA playback and DSM and deallocates memory.
  291. *
  292. * Returns:      MIDAS error code
  293. *
  294. \****************************************************************************/
  295.  
  296. int CALLING mixsdClose(void)
  297. {
  298.     int         error;
  299.  
  300.     if ( playDMA == 1 )
  301.     {
  302.         /* Stop DMA playback: */
  303.         if ( (error = dmaStop(buffer.channel)) != OK )
  304.             PASSERROR(ID_mixsdClose)
  305.     }
  306.  
  307.     /* Uninitialize Digital Sound Mixer: */
  308.     if ( (error = dsmClose()) != OK )
  309.         PASSERROR(ID_mixsdClose)
  310.  
  311.     /* Deallocate DMA buffer: */
  312.     if ( (error = dmaFreeBuffer(&buffer)) != OK )
  313.         PASSERROR(ID_mixsdClose)
  314.  
  315.     /* Deallocate post-processing table if necessary: */
  316.     if ( outputMode & sd8bit )
  317.     {
  318.         if ( (error = memFree(ppTable)) != OK )
  319.             PASSERROR(ID_mixsdClose);
  320.     }
  321.  
  322. #ifdef DUMPBUFFER
  323.     fclose(f);
  324. #endif
  325.  
  326.     return OK;
  327. }
  328.  
  329.  
  330.  
  331.  
  332. /****************************************************************************\
  333. *
  334. * Function:     int mixsdGetMode(unsigned *mode)
  335. *
  336. * Description:  Reads the current output mode
  337. *
  338. * Input:        unsigned *mode          pointer to output mode
  339. *
  340. * Returns:      MIDAS error code. Output mode is written to *mode.
  341. *
  342. \****************************************************************************/
  343.  
  344. int CALLING mixsdGetMode(unsigned *mode)
  345. {
  346.     *mode = outputMode;
  347.  
  348.     return OK;
  349. }
  350.  
  351.  
  352.  
  353.  
  354. /****************************************************************************\
  355. *
  356. * Function:     int mixsdOpenChannels(unsigned channels)
  357. *
  358. * Description:  Opens sound channels for output. Prepares post-processing
  359. *               tables, takes care of default amplification and finally opens
  360. *               DSM channels. Channels can be closed by simply calling
  361. *               dsmCloseChannels().
  362. *
  363. * Input:        unsigned channels       number of channels to open
  364. *
  365. * Returns:      MIDAS error code
  366. *
  367. \****************************************************************************/
  368.  
  369. int CALLING mixsdOpenChannels(unsigned channels)
  370. {
  371.     int         error;
  372.  
  373.     /* Open DSM channels: */
  374.     if ( (error = dsmOpenChannels(channels)) != OK )
  375.         PASSERROR(ID_mixsdOpenChannels)
  376.  
  377.     /* Take care of default amplification and calculate new post-processing
  378.        table if necessary: */
  379.     if ( channels < 5 )
  380.         mixsdSetAmplification(64);
  381.     else
  382.         mixsdSetAmplification(14*channels);
  383.  
  384.     return OK;
  385. }
  386.  
  387.  
  388.  
  389.  
  390. /****************************************************************************\
  391. *
  392. * Function:     void CalcPP8Table(void)
  393. *
  394. * Description:  Calculates a new 8-bit output post-processing table using
  395. *               current amplification level
  396. *
  397. \****************************************************************************/
  398.  
  399. static void CalcPP8Table(void)
  400. {
  401.     uchar       *tbl;
  402.     int         val;
  403.     long        temp;
  404.  
  405.     tbl = ppTable;                      /* tbl points to current table pos */
  406.  
  407.     /* Calculate post-processing table for all possible DSM values:
  408.        (table must be used with unsigned numbers - add (1 << MIX8BITS)/2 to
  409.        DSM output values first) */
  410.     for ( val = -(1 << MIX8BITS)/2; val < (1 << MIX8BITS)/2; val++ )
  411.     {
  412.         /* Calculate 8-bit unsigned output value corresponding to the
  413.            current DSM output value (val), taking amplification into
  414.            account: */
  415.         temp = 128 + ((((long) amplification) * ((long) val) / 64L) >>
  416.             (MIX8BITS-8));
  417.  
  418.         /* Clip the value to fit between 0 and 255 inclusive: */
  419.         if ( temp < 0 )
  420.             temp = 0;
  421.         if ( temp > 255 )
  422.             temp = 255;
  423.  
  424.         /* Write the value to the post-processing table: */
  425.         *(tbl++) = (uchar) temp;
  426.     }
  427. }
  428.  
  429.  
  430.  
  431.  
  432. /****************************************************************************\
  433. *
  434. * Function:     int mixsdSetAmplification(unsigned amplification)
  435. *
  436. * Description:  Sets the amplification level. Calculates new post-processing
  437. *               tables and calls dsmSetAmplification() as necessary.
  438. *
  439. * Input:        unsigned amplification  amplification value
  440. *
  441. * Returns:      MIDAS error code
  442. *
  443. \****************************************************************************/
  444.  
  445. int CALLING mixsdSetAmplification(unsigned _amplification)
  446. {
  447.     int         error;
  448.  
  449.     amplification = _amplification;
  450.  
  451.     if ( outputMode & sd8bit )
  452.     {
  453.         /* 8-bit output mode - do not set amplification level using DSM,
  454.            but calculate a new post-processing table instead: */
  455.         CalcPP8Table();
  456.     }
  457.     else
  458.     {
  459.         /* Set amplification level to DSM: */
  460.         if ( (error = dsmSetAmplification(amplification)) != OK )
  461.             PASSERROR(ID_mixsdSetAmplification)
  462.     }
  463.  
  464.     return OK;
  465. }
  466.  
  467.  
  468.  
  469.  
  470. /****************************************************************************\
  471. *
  472. * Function:     int mixsdGetAmplification(unsigned *amplification);
  473. *
  474. * Description:  Reads the current amplification level. (DSM doesn't
  475. *               necessarily know the actual amplification level if
  476. *               post-processing takes care of amplification)
  477. *
  478. * Input:        unsigned *amplification   pointer to amplification level
  479. *
  480. * Returns:      MIDAS error code. Amplification level is written to
  481. *               *amplification.
  482. *
  483. \****************************************************************************/
  484.  
  485. int CALLING mixsdGetAmplification(unsigned *_amplification)
  486. {
  487.     *_amplification = amplification;
  488.  
  489.     return OK;
  490. }
  491.  
  492.  
  493.  
  494.  
  495. /****************************************************************************\
  496. *
  497. * Function:     int mixsdSetUpdRate(unsigned updRate);
  498. *
  499. * Description:  Sets the channel value update rate (depends on song tempo)
  500. *
  501. * Input:        unsigned updRate        update rate in 100*Hz (eg. 50Hz
  502. *                                       becomes 5000).
  503. *
  504. * Returns:      MIDAS error code
  505. *
  506. \****************************************************************************/
  507.  
  508. int CALLING mixsdSetUpdRate(unsigned updRate)
  509. {
  510.     /* Calculate number of elements to mix between two updates: (even) */
  511.     mixLeft = updateMix = ((unsigned) ((100L * (ulong) mixRate) /
  512.         ((ulong) updRate)) + 1) & 0xFFFFFFFE;
  513.  
  514.     return OK;
  515. }
  516.  
  517.  
  518.  
  519.  
  520. /****************************************************************************\
  521. *
  522. * Function:     int mixsdStartPlay(void)
  523. *
  524. * Description:  Prepares for playing - reads DMA playing position. Called
  525. *               once before the Sound Device and music player polling loop.
  526. *
  527. * Returns:      MIDAS error code
  528. *
  529. \****************************************************************************/
  530.  
  531. int CALLING mixsdStartPlay(void)
  532. {
  533.     int         error;
  534.  
  535.     /* Read DMA playing position: */
  536.     if ( (error = dmaGetPos(&buffer, &dmaPos)) != OK )
  537.         PASSERROR(ID_mixsdStartPlay)
  538.  
  539.     return OK;
  540. }
  541.  
  542.  
  543.  
  544.  
  545. /****************************************************************************\
  546. *
  547. * Function:     int mixsdPlay(int *callMP);
  548. *
  549. * Description:  Plays the sound - mixes the correct amount of data with DSM
  550. *               and copies it to DMA buffer with post-processing.
  551. *
  552. * Input:        int *callMP             pointer to music player calling flag
  553. *
  554. * Returns:      MIDAS error code. If enough data was mixed for one updating
  555. *               round and music player should be called, 1 is written to
  556. *               *callMP, otherwise 0 is written there. Note that if music
  557. *               player can be called, mixsdPlay() should be called again
  558. *               with a new check for music playing to ensure the DMA buffer
  559. *               gets filled with new data.
  560. *
  561. \****************************************************************************/
  562.  
  563. int CALLING mixsdPlay(int *callMP)
  564. {
  565.     unsigned    *mixBuf;
  566.     int         error;
  567.     int         numElems, mixNow;
  568.     int         dsmBufSize;
  569.     int         dmaBufLeft;
  570. #ifdef DUMPBUFFER
  571.     unsigned    oldPos;
  572. #endif
  573.  
  574.     /* Calculate number of bytes that fits in the buffer: */
  575.     if ( dmaPos >= mixPos )
  576.         numElems = dmaPos - mixPos - 16;
  577.     else
  578.         numElems = dmaBufferSize - mixPos + dmaPos - 16;
  579.  
  580.     /* Do not mix less than 16 bytes: */
  581.     if ( numElems < 16 )
  582.     {
  583.         *callMP = 0;
  584.         return OK;
  585.     }
  586.  
  587.     /* Calculate actual number of elements: */
  588.     if ( outputMode & sdStereo )
  589.         numElems >>= 1;
  590.     if ( outputMode & sd16bit )
  591.         numElems >>= 1;
  592.  
  593.     /* Make sure number of elements is even: */
  594.     numElems = numElems & 0xFFFFFFFE;
  595.  
  596.     /* Check that we won't mix more elements than there is left before next
  597.        update: */
  598.     if ( numElems > mixLeft )
  599.         numElems = mixLeft;
  600.  
  601.     /* Calculate DSM mixing buffer size in elements: (FIXME) */
  602.     dsmBufSize = dsmMixBufferSize;
  603. #ifdef __32__
  604.     dsmBufSize >>= 2;
  605. #else
  606.     dsmBufSize >>= 1;
  607. #endif
  608.     if ( outputMode & sdStereo )
  609.         dsmBufSize >>= 1;
  610.  
  611.     /* Decrease number of elements before next update: */
  612.     mixLeft -= numElems;
  613.  
  614.     /* Mix all the data and post-process it to the DMA buffer, making sure
  615.        DSM mixing buffer will not overflow: */
  616.     while ( numElems )
  617.     {
  618.         /* mixNow = number of elements to mix in this round: */
  619.         if ( numElems > dsmBufSize )
  620.             mixNow = dsmBufSize;
  621.         else
  622.             mixNow = numElems;
  623.  
  624.         /* Decrease number of elements left: */
  625.         numElems -= mixNow;
  626.  
  627.         /* Mix the data: */
  628.         if ( (error = dsmMixData(mixNow)) != OK )
  629.             PASSERROR(ID_mixsdPlay)
  630.  
  631.         /* Point mixBuf to current mixing buffer position: */
  632.         mixBuf = dsmMixBuffer;
  633.  
  634.         /* Calculate the number of elements left in DMA buffer before
  635.            buffer end: */
  636.         dmaBufLeft = dmaBufferSize - mixPos;
  637.         if ( outputMode & sdStereo )
  638.             dmaBufLeft >>= 1;
  639.         if ( outputMode & sd16bit )
  640.             dmaBufLeft >>= 1;
  641.  
  642.         /* If DMA buffer end would be reached, first process the data up
  643.            to the buffer end: */
  644.         if ( dmaBufLeft < mixNow )
  645.         {
  646. #ifdef DUMPBUFFER
  647.             oldPos = mixPos;
  648. #endif
  649.             postProc(dmaBufLeft, (uchar*) buffer.dataPtr, mixPos, mixBuf,
  650.                 ppTable);
  651. #ifdef DUMPBUFFER
  652.             fwrite(((uchar*) buffer.dataPtr) + oldPos, dmaBufLeft, 1, f);
  653. #endif
  654.             mixNow -= dmaBufLeft;
  655.             mixPos = 0;
  656.             if ( outputMode & sdStereo )
  657.                 mixBuf += 2*dmaBufLeft;
  658.             else
  659.                 mixBuf += dmaBufLeft;
  660.         }
  661.  
  662.         /* Process the rest of the data and update mixing position: */
  663. #ifdef DUMPBUFFER
  664.         oldPos = mixPos;
  665. #endif
  666.         mixPos = postProc(mixNow, (uchar*) buffer.dataPtr, mixPos, mixBuf,
  667.             ppTable);
  668. #ifdef DUMPBUFFER
  669.         fwrite(((uchar*) buffer.dataPtr) + oldPos, mixNow, 1, f);
  670. #endif
  671.  
  672.         if ( mixPos >= dmaBufferSize )
  673.             mixPos = 0;
  674.     }
  675.  
  676.     /* Check if music player should be called: */
  677.     if ( mixLeft == 0 )
  678.     {
  679.         mixLeft = updateMix;
  680.         *callMP = 1;
  681.     }
  682.     else
  683.     {
  684.         *callMP = 0;
  685.     }
  686.  
  687.     return OK;
  688. }
  689.  
  690.  
  691. /*
  692.  * $Log: mixsd.c,v $
  693.  * Revision 1.2  1997/01/16 18:41:59  pekangas
  694.  * Changed copyright messages to Housemarque
  695.  *
  696.  * Revision 1.1  1996/05/22 20:49:33  pekangas
  697.  * Initial revision
  698.  *
  699. */